#include "executor.h"
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

void thprintln(const char* msg, int error = 0) {
    //return;
    if (error) {
        printf("%u] %s. error=%i\n", pthread_self(), msg, error);
    } else {
        printf("%u] %s.\n", pthread_self(), msg);
    }
}

class SimpleJob : public Job {
    public:
        Job* execute() {
	    thprintln("I'm a simple job!");
            return NULL;
        }
};

/*
int main() {
    thprintln("hi! I'm main thread!");
    Executor* e = new Executor(100);
    //e->startAll();
    sleep(5);
    e->schedule(new SimpleJob());
    sleep(2);
    delete e;
}*/

int Job::priority() {
    return JOB_DEFAULT_PRIORITY;
}

Executor::Executor(int pool_size) {
    thprintln("1");
    this->pool_limit = pool_size;
    thprintln("2");
    this->pool_size = 0;
    thprintln("3");
    this->threads = new Thread *[pool_size];
    thprintln("4");
    this->lock = new Lock();
    thprintln("5");
    this->running = true;
}

void Executor::startAll() {
    thprintln("starting all threads");
    lock->lock();
    while (inc());
    lock->unlock();
}

bool Executor::inc() {
    if (pool_size >= pool_limit) {
        return false;
    }

    threads[pool_size] = new Thread(this);
    threads[pool_size]->start();
    pool_size++;
}

void Executor::run() {
    thprintln("run");
    while (running) {
        lock->lock();
        Job* job = next();
        printf("%u] next is %i\n", pthread_self(), job);

        if (job == NULL) {
	    lock->wait();
	    lock->unlock();
	    continue;
	}
	lock->unlock();
	thprintln("executing next...");
        Job* next = job->execute();
        if (next != NULL) {
            schedule(next);
        }
	thprintln("executed");
        if (next != job) {
	    delete job;
        }
    }
    thprintln("done."); 
}

void Executor::schedule(Job* job) {
    thprintln("scheduling job...");
    lock->lock();
    if (needInc()) {
        inc();
    }
    job_queue.push(job);
    lock->notify();
    lock->unlock();
    thprintln("scheduled");
}

bool Executor::needInc() {
    return pool_size == 0 || !job_queue.empty();
}

Job* Executor::next() {
    if (job_queue.empty()) {
        return NULL;
    }

    Job* job = job_queue.front();
    job_queue.pop();

    return job;
}

Executor::~Executor() {
    delete [] threads;
    delete lock;
}

Lock::Lock() {
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
}

Lock::~Lock() {
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
}

void Lock::lock() {
    thprintln("locking...");
    int error = pthread_mutex_lock(&mutex);
    thprintln("locked", error);
}

bool Lock::tryLock() {
    thprintln("trying lock...");
    int error = pthread_mutex_trylock(&mutex);
    if (error != 0) {
        thprintln("try lock failed", error);
	return false;
    }
    thprintln("locked", error);
    return true;
}

void Lock::unlock() {
    thprintln("unlocking...");
    int error = pthread_mutex_unlock(&mutex);
    thprintln("unlocked", error);
}

void Lock::wait() {
    thprintln("waiting...");
    int error = pthread_cond_wait(&cond, &mutex);
    thprintln("awaken!", error);
}

void Lock::wait(long millis) {
    thprintln("timed waiting... not implemented");
}

void Lock::notifyAll() {
    thprintln("notifyAll");
    int error = pthread_cond_broadcast(&cond);
    thprintln("all notified", error);
}

void Lock::notify() {
    thprintln("notify");
    int error = pthread_cond_signal(&cond);
    thprintln("notified", error);
}

Thread::Thread(Runnable* runnable) {
    this->runnable = runnable;
}

Thread::~Thread() {
}

void Thread::start() {
    pthread_create(&id, NULL, &srun, this);
}

void* Thread::srun(void* thread) {
    Thread* th = ((Thread*)thread);
    if (th->runnable == NULL) {
        th->run();
    } else {
        th->runnable->run();
    }
}

void Thread::run() {thprintln("run() should be overriden!");}

